package com.idea.relax.redis.lock.client;

import com.idea.relax.redis.lock.support.LockType;
import com.idea.relax.redis.lock.support.CheckedSupplier;
import com.idea.relax.redis.support.utils.RelaxUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;

import java.util.concurrent.TimeUnit;

/**
 * @className: RedisLockClientImpl
 * @description:
 * @author: salad
 * @date: 2022/9/2
 **/
@Slf4j
@AllArgsConstructor
public class RedisLockClientImpl implements RedisLockClient {

    private final RedissonClient redissonClient;

    /**
     * 尝试获取锁
     * @param lockName
     * @param lockType
     * @param waitTime
     * @param leaseTime
     * @param timeUnit
     * @return
     * @throws InterruptedException
     */
    @Override
    public boolean tryLock(String lockName, LockType lockType,
                           long waitTime, long leaseTime, TimeUnit timeUnit) throws InterruptedException {
        RLock lock = this.getLock(lockName, lockType);
        return lock.tryLock(waitTime, leaseTime, timeUnit);
    }

    /**
     * 释放锁
     * @param lockName
     * @param lockType
     */
    @Override
    public void unLock(String lockName, LockType lockType) {
        RLock lock = this.getLock(lockName, lockType);
        if (lock.isLocked() && lock.isHeldByCurrentThread()) {
            lock.unlock();
        }

    }

    private RLock getLock(String lockName, LockType lockType) {
        RLock lock;
        if (LockType.REENTRANT == lockType) {
            lock = this.redissonClient.getLock(lockName);
        } else {
            lock = this.redissonClient.getFairLock(lockName);
        }

        return lock;
    }

    /**
     * 加锁
     * @param lockName
     * @param lockType
     * @param waitTime
     * @param leaseTime
     * @param timeUnit
     * @param target
     * @param <T>
     * @return
     */
    @Override
    public <T> T lock(String lockName, LockType lockType, long waitTime,
                      long leaseTime, TimeUnit timeUnit, CheckedSupplier<T> target) {
        try {
            //尝试加锁
            boolean status = this.tryLock(lockName, lockType, waitTime, leaseTime, timeUnit);
            if (status) {
                //加锁成功就执行目标方法
                return target.get();
            }
        } catch (Throwable e) {
            throw RelaxUtil.unchecked(e);
        } finally {
            //释放锁
            this.unLock(lockName, lockType);
        }
        return null;
    }

}
